E-도커 파일 구조 탐색
전체 디렉토리
기본적으로 /var/lib/docker
에 데이터들이 존재한다.
크기를 보면 대충 overlay2가 가장 크다는 것을 알 수 있을 것이다.
buildkit
도커 buildkit 참조.
S-도커 db 확장자 장애를 보면 알 수 있듯 여기에 있는 데이터 잘못 건드리면 난리나니까 조심하도록 한다.
overlay2
실제 스토리지로 활용되는 디렉터리이다.
말 그대로, 도커를 띄우는데 필요한 모든 데이터가 다 들어있다.
이중에서 init이 붙는 녀석은 컨테이너 개수만큼 존재한다.
즉, 컨테이너 생성 시 씌워지는 thin layer라는 것이다.
나머지는.. 뭐 딱 보면 알겠지만 이미지들을 만들 때 쓰이는 레이어들에 대한 디렉토리이다.
이중에서 l이라는 디렉토리는 심볼릭 링크를 나타낸다.
여기에는 또다른 해시 값들이 각 레이어들의 실제 내용, diff 디렉토리들을 담고 있다.
이 심볼릭 링크가 어떻게 쓰이는지를 보기 이전에 각 레이어 디렉토리를 살펴본다.
내가 띄워둔 컨테이너의 레이어 디렉터리를 들어가보았다.
init은 컨테이너 생성 직전에 생기는 최종 이미지 디렉터리이고, 실제 컨테이너 디렉터리는 init이 떼어진 디렉터리이다.
각 디렉터리에 대한 설명이다.
- diff
- 컨테이너 생성 이후에 변화하는 부분이다.
- 정확하게는 현재 레이어에서 발생되는 변화를 가리킨다.
- 내가 내부에 들어가 마음대로 testdir, testfile을 만들었었는데 그게 반영된 게 보인다.
- work
- 도커가 컨테이너를 생성할 때 작업용으로 생성하는 디렉터리
- 통합 뷰의 원자성을 위해 생성된다고는 하는데, 잘은 모르겠다
- 실질적으로는 파일을 가지지 않게 된다고 한다.
- merged
- 모든 이미지와 컨테이너 생성 후 생긴 파일을 다 합친 최종 형태
- 컨테이너에 들어가면 접하게 되는 실제 내부와 정확하게 일치한다.
내부 파일의 생김새를 보면 알 수 있듯, 이 녀석들이 실제 컨테이너의 내부를 구성하는 파일들인 것이다.
이 디렉들이 역할은 컨테이너를 inspect해서도 알 수 있다.
이 이미지에서 LowerDir
이 엄청 많고, 찾기 어려운 것처럼 보인다.
안 그래도 레이어는 덮어씌워진다 했는데, 왜 그 덮어씌워질 아래 디렉터리가 없는 것이냐!
그것은 lower 파일을 통해서 얻어진다.
이게 lower 파일인데, 안 속에 l 디렉의 파일들을 가리키고 있다.
이제야 l 디렉의 내부를 볼 타이밍이 왔다.
그 안에는 또 해시된 어떤 심볼릭 링크가 위치하고 있다.
이 심볼릭 링크는 각 레이어의 결과물인 diff를 가리키고 있다.
정리해보자면, 이렇다.
한 레이어는 자신의 lower 파일에 해당하는 것을 참조할 때 이를 통해서 빠르게 레이어를 쌓고 merged 디렉을 만든다.
스스로를 l디렉에 저장되는 해시값 정보를 link 파일에 남기는 것.
왜 굳이 이렇게 해시값을 만들어 l에 저장할까?
아래는 내 견해이다.
- 읽기 전용 보장
- diff에 담긴 레이어의 상태가 변하지 않았음을 보장하는 좋은 방법 중 하나는 전체를 이용한 해시값을 만드는 것이다.
- 이 경우 누군가 악의적으로 조금이라도 파일을 수정하거나, 오류가 발생하여 이전과 같이 레이어가 쌓이지 않은 순간 해시 값이 바뀌게 된다.
- 중복 레이어 방지
- 각 레이어가 고유한 해시값을 가진다면, 중복을 막아 불필요한 디스크 사용을 방지할 수 있다.
- 빠른 접근
- lower 디렉의 모든 정보를 일일히 파일로 남기는 건 비효율적
- 그래서 마치 파이썬의 dict 타입이나 자바의 HashMap처럼 키값쌍을 두어 빠르게 접근하고자 하는 것이다.
참고로, 컨테이너가 되지 않은 레이어들은 이렇게 생겼다.
각 레이어가 가진 그 자체의 정보는 전부 diff에 있다.
merged가 된 놈이 없는데, 실제 이미지가 될 놈만 merge가 되면 되니까 그런 듯하다.
아무튼 저렇게 작은 dev, etc단위의 변화가 점차 모여서 하나의 큰 컨테이너를 완성하는 것이다.
committed는 commit 기능을 이야기한다.
현재 컨테이너 상태를 commit 한다는 것은 레이어화를 시킨다는 뜻인데, 말그대로 이 레이어가 commit됐다는 것을 말하는 정도의 의미에 불과하다.
달리 말해, 읽기 전용 상태가 됐다를 보장할 수 있는 녀석이기도 하다.
하는 김에 새로 pull을 받아서도 확인해보자
보다시피 새로운 레이어가 들어왔는데, 해당 레이어는 스토리지에 잘 들어왔다.
맨 아래 다이제스트는 태그와 관련된 정보이다.
즉, 위에서 본 image쪽의 repository.json 파일에 담겨있고, 저 다이제스트가 어떤 이미지에 해당하는지에 대한 정보도 담겨있다.
image
직관적인 이름..
이미지들에 대한 정보를 담는다.
하지만 아까 봤듯, 실제 데이터는 전부 overlay2라는 디렉에 저장되고, 이 녀석은 빠르게 이미지 정보를 꺼내기 위해 존재하는 녀석 정도이다.
스토리지는 다양할 수 있기 때문에, 내부에 overlay2라는 디렉이 있다.
만약 내가 스토리지 방식을 aufs로 만드는 설정을 했다면 aufs 디렉이 추가됐을 것이다.
이게 이미지 디렉 안 속의 정보이다.
기본적으로 이미지의 디렉이 있고, 이미지를 구성하는 레이어들의 정보가 있다.
distribution
잠깐 distribution이라는 디렉을 먼저 훑자면, 그냥 데이터를 빠르게 조회할 수 있도록 키값 쌍을 저장해두는 디렉이다.
이름은 저렇게 돼있는데, 직접 만져보니까 v2 -> diff -> digest 관계로 키값을 조회할 수 있었다.
근데 왜 by인걸까? to라고 하는 게 맞지 않나..?
그건 이제 도커 개발자들이 알 만한 내용인 것 같다.
repositories.json
원하는 데이터에 더 빠르게 접근할 수 있도록 또 파일이 있다.
기본적으로 빠르게 이미지를 찾을 수 있도록 json이 형성되어 있다.
태그 정보를 찾을 수 있도록 층위가 한 칸 더 들어간다.
이게 중요한 것이, 사용자가 docker pull
등의 이미지와 관련된 동작을 할 때 Digest라면서 표시되는 해시 값이 여기에 나오는 해시값이다.
한 이미지에 대해서도 다양한 태그와 버전이 존재할 수 있으니, 이러한 접근은 매우 합당하다.
imagdb
content와 metadata가 나뉘는 이유는 잘 모르겠다.
이것도 개발자한테 물어봐야할 듯.
이미지들의 해시 값이 imaged 안에 있다.
해당 해시 파일들을 까보면 이미지를 inspect하는 것과 비슷한 값이 나온다.
각각은 파일이고, 이러한 정보를 담고 있다.
여기에는 도커파일을 빌드할 당시의 정보도 담겨 있어, 이미지를 직접 만드는 개발자라면 기본적으로 알아두면 좋을 포인트이다.
layerdb
그보다 sha256에 더 깊이 들어가보면 레이어들에 대한 정보가 들어있다.
알아보기는 어렵지만, 각 레이어의 정보를 확실히 담고 있다.
가지고 있는 것이 전부 해시 값인 것을 확인할 수 있다.
- parent
- 부모 레이어 데이터의 해시값
- diff
- 현재 레이어의 변경점들을 담은 데이터의 해시값
- cached-id
- 캐시된 데이터의 해시값
- size
- 크기
이름으로도 알 수 있는 꽤나 직관적인 상황.
- 크기
잠시 정리하자면, images라는 디렉에는 어떤 스토리지를 쓸지에 따라 데이터가 저장된다.
실제 데이터는 해당 스토리지 디렉에 저장될 것이고 여기에는 각 이미지와 레이어의 해시값만 저장되는 장소인 것이다.
빠르게 컨테이너를 실행하고 불러오기 위한 해시테이블이 들어있는 것이다.
그러한 데이터들은 sha256에, 실제로 컨테이너가 띄워질 때는 mounts 디렉에도 들어가게 된다.
컨테이너 inspect를 할 때 레이어로 추정되는 이 녀석들은 무엇인가?
여기에 나오는 해시값은 전부 overlay2 디렉의 해시값이다.
확인해보니까 다 일일히 있더라..
그럼 도대체 이 image라는 디렉은 왜 존재하는 거야?
내 견해는 이렇다.
무거운 overlay2 디렉터리 때문이다.
근데 사용자가 docker ps
등의 명령어를 쉴 새 없이 날려댄다.
그때마다 일일히 디렉터리를 뒤져대는 건 비효율적이라 일종의 캐싱 역할로 각종 정보를 남겨두는것이다.
개인적으로는 도커가 초반에 개발된 이후로 더 효율적인 구조로 바꾸지 못한 잔재로 보이기도 한다.
뭣하러 overlay2에, 여기에 따로따로 캐싱이 존재하나.
containerd가 분리될 정도의 리팩터링을 거칠 수 있었음에도 바꾸지 않았단 것은 오히려 이럴 수밖에 없는 이유가 있었다는 것일 수도 있다.
containers
현재 추적되는 컨테이너들에 대한 정보가 담긴다.
몇 가지는 단순하게 알 수 있다.
- *json.log
- 로그 파일을 저장하는 파일
- 이걸 통해서 우리는
docker logs
를 쓸 수 있는 듯
- hostname
docker ps
를 칠 때 나오는 12자리 해시값.- 내부에서도 확인할 수 있다.
- hosts
- 컨테이너 내부에 들어가게 되는 hosts 파일
- 참고로 같은 네트워크에 있는 컨테이너라도 hosts에 정보가 담기지는 않는다.
--host
옵션을 사용할 때를 위해 존재하는 듯하다.- 아마 언젠가 deprecated될 거라는 것에 내 수염 한가닥을 걸겠다.
- *.json
- host, config 정보들은 컨테이너 세부 스펙을 다룬다.
- 흔히 inspect로 찾아볼 수 있는 내용들이다.
- image 디렉에 존재하는 정보 방식과 다르지 않다.
- resolve.conf
- resolve.conf는 DNS 관련 사전 설정 정보를 담는다.
nmcli dev show
를 통해 내 호스트 환경의 dns를 확인해보니 KT의 서버가 나왔다.- 컨테이너가 세워질 때도 이쪽에 대한 정보가 담기게 되며, 그렇기에 컨테이너 내부에서 문제 없이 dns를 사용할 수 있는 것이다.
- 참고로 DNS는
resolvectl
을 통해 리졸버를 탐구할 수 있다./etc/resolve.conf
를 가보면 재밌는 내용들을 또 찾을 수 있는데, 127.0.0.53이 로컬 리졸버 서버로 활용된다고 한다.- DNS 탐구를 하기 위해 시작한 것은 아니니까 이건 나중에 다루도록 한다.
- checkpoints
- 도커에는 checkpoint기능이 있는데, 그것에 대한 정보가 checkpoints 디렉에 저장된다고 한다.
- mounts
- 잘 모르겠다.
- 볼륨 마운팅을 아무리 해도 아무런 일이 안 생긴다.
- volume 기능을 사용하기 이전에 혹시 기능이 있었을까?
- network가 나오고 host 옵션이 바보가 되었듯이..